package cn.song.docflow.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.pdf.PDFParser;
import org.apache.tika.sax.BodyContentHandler;
import org.xml.sax.SAXException;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

/**
 * Tika 文本提取器（仅PDF专用，禁用外部解析器）
 *
 * @author SongRenShuo
 * @date 2025/09/14
 */
@Slf4j
public class TikaTextExtractor {
    /**
     * 主方法，用于提取PDF文件中的文本内容和元数据
     * <p>
     * 该方法会：
     * 1. 设置系统属性禁用外部解析器
     * 2. 定义输入输出文件路径
     * 3. 校验输入文件是否存在
     * 4. 使用PDFParser解析PDF文件
     * 5. 将提取的文本和元数据写入到文件中
     * </p>
     *
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 1. 【关键】提前设置系统属性，禁用外部解析器检查（在所有Tika类初始化前执行）
        System.setProperty("org.apache.tika.parser.external.check", "false");
        // （可选）移除无关属性sun.java2d.cmm（与外部解析器无关，若无需解决2D渲染问题可删除）
        // System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider")

        // 2. 定义文件路径
        File inputFile = new File("exports/测试文档.pdf");
        File outputFile = new File("exports/extracted_text.txt");
        File metadataFile = new File("exports/metadata.txt");

        // 3. 校验输入文件是否存在
        if (!inputFile.exists()) {
            log.error("输入文件不存在：{}", inputFile.getAbsolutePath());
            return;
        }

        try (InputStream input = Files.newInputStream(inputFile.toPath())) {
            // 4. 【关键】不使用Tika默认解析器，直接初始化PDF专用解析组件
            // -1表示无大小限制
            BodyContentHandler handler = new BodyContentHandler(-1);
            Metadata metadata = new Metadata();
            ParseContext context = new ParseContext();
            // 明确指定仅使用PDFParser，排除所有其他解析器
            context.set(org.apache.tika.parser.Parser.class, new PDFParser());

            // 5. 解析PDF（仅调用PDFParser，无外部解析器参与）
            PDFParser parser = new PDFParser();
            parser.parse(input, handler, metadata, context);

            // 6. 写入结果
            writeTextToFile(handler.toString(), outputFile);
            writeMetadataToFile(metadata, metadataFile);

            log.info("文本已保存到: {}", outputFile.getAbsolutePath());
            log.info("元数据已保存到: {}", metadataFile.getAbsolutePath());

        } catch (IOException | SAXException | TikaException e) {
            log.error("PDF解析失败", e);
        }
    }

    /**
     * 将提取的文本写入到指定文件中
     *
     * @param text       需要写入的文本内容
     * @param outputFile 输出文件对象
     */
    private static void writeTextToFile(String text, File outputFile) {
        try (BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(Files.newOutputStream(outputFile.toPath()), StandardCharsets.UTF_8))) {
            writer.write(text);
        } catch (IOException e) {
            log.error("写入文本文件出错: {}", e.getMessage(), e);
        }
    }

    /**
     * 将元数据信息写入到指定文件中
     *
     * @param metadata     包含PDF文件元数据的对象
     * @param metadataFile 输出元数据文件对象
     */
    private static void writeMetadataToFile(Metadata metadata, File metadataFile) {
        try (BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(Files.newOutputStream(metadataFile.toPath()), StandardCharsets.UTF_8))) {
            writer.write("Metadata:\n");
            for (String name : metadata.names()) {
                writer.write(name + ": " + metadata.get(name) + "\n");
            }
        } catch (IOException e) {
            log.error("写入元数据文件出错: {}", e.getMessage(), e);
        }
    }
}